【优化】IF 您所在的位置:网站首页 java ifelse优化 【优化】IF

【优化】IF

2024-07-13 15:00| 来源: 网络整理| 查看: 265

1. 前言

代码中,如果 if-else 语句比较多,阅读起来比较困难,维护性较差,很容易出bug。接下来,此文将介绍优化 if-else 代码的七种方案:

在这里插入图片描述

2. 优化方案 2.1 提前 return,去除不必要的 else

如果 if-else 代码块包含 return 语句,可以考虑通过提前 return,把多余 else 干掉,使代码更加优雅

优化前:

if (condition) { // TODO } else { return; }

优化后:

if (!condition) { return; } // TODO 2.2 使用条件三目运算符

使用条件三目运算符可以简化某些 if-else,使代码更加简洁,更具有可读性

优化前:

int status = 0; if (condition) { status = 1; } else { status = 2; }

优化后:

int satus = condition ? 1 : 2 2.3 使用枚举

在某些时候,使用枚举也可以优化 if-else 逻辑分支(也可以看做一种表驱动方法)

优化前:

String orderStatusDes = ""; if (orderStatus == 0) { orderStatusDes = "订单未支付"; } else if (orderStatus == 1) { orderStatusDes = "订单已支付"; } else if (orderStatus == 2) { orderStatusDes = "已发货"; }

优化后: 定义一个枚举类:

public enum OrderStatusEnum { NOT_EXIST(-1, "不存在") , NOT_PAID(0, "订单未支付") , PAID(1, "订单已支付") , SENDED(2, "已发货"); private Integer code; private String message; OrderStatusEnum(Integer code, String message) { this.code = code; this.message = message; } // 枚举列类中定义一个自定义方法,但如果要想能够被外面访问,需要定义成 static 类型 public static OrderStatusEnum getOrderStatusEnum(Integer status) { for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) { if (status.equals(statusEnum.getMessage())) { return statusEnum; } } return NOT_EXIST; } }

有了枚举之后,以上 if-else 逻辑分支,可以优化为:

String orderStatusDes = OrderStatusEnum.getOrderStatusEnum(orderStatus).getMessage();

如果一个工程中,定义的枚举类较多,可以实现一个共同的接口

2.4 合并条件表达式

如果有一系列条件返回一样的结果,可以将它们合并为一个条件表达式,让逻辑更加清晰

优化前:

if (age return 100; } // TODO

优化后:

if (age return 0.0; } if (rate > 0 && duration > 0) { return (income / duration) * rate; } return 0.0;

优化后:

if (captial return 0.0; } return (income / duration) * rate; 2.6 使用 Optional

有时候 if-else 比较多,是因为非空判断导致的,这时候你可以使用 java8 的 Optional 类进行优化

优化前:

if (user != null) { if (user.getCity() != null) { if (user.getCity().getCode() != null) { String value = user.getCity().getCode().getValue(); } } }

优化后:

Optional.ofNullable(user).map(User::getCity).map(City::getCode).map(Code::getValue).orElse("init"); 2.7 策略模式 + 工厂方法消除 if else

根据字段名(年龄、姓名、出生地)不同,将对象集合进行排序(可以升序、降序)

优化前:

// 属性值 String props = "name"; // 升序、降序 String order = "ascending"; List users = new ArrayList(); // 将 users 进行赋值 if ("age".equals(props )) { if ("ascending".equals(order)) { // 升序排序 } else { // 降序排序 } } else if ("name".equals(props)) { if ("ascending".equals(order)) { // 升序排序 } else { // 降序排序 } } else if ("birthPlace".equals(props)) { if ("ascending".equals(order)) { // 升序排序 } else { // 降序排序 } }

优化后:

把每个条件逻辑代码块,抽象成一个公共的接口:

public interface PropService { // 升序 void sortAscend(List users); // 降序 void sortDescend(List users); }

根据每个逻辑条件,定义相对应的策略实现:

public class NamePropServiceImpl implements PropService { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getName)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getName).reversed()); } } public class AgePropServiceImpl implements PropService { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getAge)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getAge).reversed()); } } public class BirthPlacePropServiceImpl implements PropService { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getBirthPlace)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getBirthPlace).reversed()); } }

再定义策略工厂类,用来管理这些属性实现策略类,如下:

public class PropServiceFactory { private static final Map propServiceMap = new ConcurrentHashMap(); static { propServiceMap.put("name", new NamePropServiceImpl()); propServiceMap.put("age", new AgePropServiceImpl()); propServiceMap.put("birthPlace", new BirthPlacePropServiceImpl()); } public static PropService getPropService(String props) { return propServiceMap.get(props); } }

使用了策略 + 工厂模式之后,代码变得简洁多了,如下:

public class Test { public static void main(String[] args) { String props = "name"; List user = new ArrayList(); PropService propService = PropServiceFactory.getPropService(props); // 升序 propService.sortAscend(user); // 降序 propService.sortDescend(user); } }

在工厂类中,我们需要自己手动将属性策略类添加到一个 Map 中去,那么,当我们需要添加一个新的属性名排序时,就需要修改工厂类的源代码了,所以,这里可以进一步改造!!

2.7.1 改造1:使用 Spring

在 Spring 初始化时,需要将策略模式类都注册到工厂类中,将策略类进行改造,使用 Spring 的 InitializingBean 接口,在 bean 初始化后,执行 afterPropertiesSet() 方法进行注册。代码如下:

public class NamePropServiceImpl implements PropService, InitializingBean { @Override public void sortAscend(List users) { Collections.sort(users, Comparator.comparing(User::getName)); } @Override public void sortDescend(List users) { Collections.sort(users, Comparator.comparing(User::getName).reversed()); } @Override public void afterPropertiesSet(){ PropServiceFactory.register("name", this); } }

其它与之相似,这里就省略了。

工厂类稍作改变:

public class PropServiceFactory { private static final Map propServiceMap = new ConcurrentHashMap(); public static void register(String props, PropService propService){ Assert.notNull(props, "Props can't be null"); propServiceMap.put(props, PropService); } public static PropService getPropService(String props) { return propServiceMap.get(props); } } 2.7.2 改造2:使用 枚举 + 反射

属性策略实现类都不变

添加一个枚举:

public enum PropServiceEnum { NAME_PROP("com.zzc.service.NamePropServiceImpl", "名称") , AGE_PROP("com.zzc.service.AgePropServiceImpl", "年龄") , BIRTH_PLACE_PROP("com.zzc.service.BirthPlacePropServiceImpl", "出生地") ; // 类的全限定名 private String propValue; private String propName; PropServiceEnum(String propValue, String propName) { this.propValue = propValue; this.propName = propName; } public String getPropValue() { return propValue; } public String getPropName() { return propName; } }

修改工厂类:

public class PropServiceFactory { public static PropService getPropService(String props) { String className = PropServiceEnum.valueOf(props).getClassName(); return (PropService) Class.forName(className).newInstance(); } }

=========================================================================================== 2021-12-09 更:

2.8 Spring + 策略模式 + 自定义注解

优化前:

if ("文本" == msgType) { // TODO } else if ("图片" == msgType) { // TODO } else if ("视频" == msgType) { // TODO } else { // TODO }

根据消息的不同类型有不同的处理策略,如果都放在这种 if else 代码块中,代码很难维护,也很丑。所以,这里就用了策略模式来处理这种情况。

策略模式:就是定义一个接口,然后有多个实现类,每种实现类封装了一种行为。然后根据条件的不同选择不同的实现类。

优化后:

定义一个消息对象:

@Data @NoArgsConstructor @AllArgsConstructor public class MsgVo { private Integer type; private String content; }

定义一个消息类型的枚举类:

public enum MsgTypeEnum { TEXT(1, "文本") , IMAGE(2, "图片") , VIDEO(3, "视频") ; private Integer type; private String msg; MsgTypeEnum(Integer type, String msg) { this.type = type; this.msg = msg; } }

定义一个消息处理接口:

public interface MsgService { void handler(MsgVo msgVo); }

处理文本消息实现类:

@Service public class TextMsgServiceImpl implements MsgService { @Override public void handler(MsgVo msgVo) { System.out.println("处理文本消息:" + msgVo.getContent()); } }

处理图片消息实现类:

@Service public class ImageMsgServiceImpl implements MsgService { @Override public void handler(MsgVo msgVo) { System.out.println("处理图片消息:" + msgVo.getContent()); } }

我们也可以使用一个 Map 来维护消息类型–消息处理对象关系。这样直接根据消息类型就能拿到消息处理对象,调用消息处理对象的方法即可。

但是我们不想手动维护这个 Map 对象,因为每次增加新的消息处理类,Map 的初始化过程就得修改。

这里使用了 自定义注解 + ApplicationListener 来保存这种映射关系:

自定义注解 MsgTypeHandler:

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MsgTypeHandler { MsgTypeEnum value(); }

然后,给每一个类型添加上这个注解类型:

@Service @MsgTypeHandler(value = MsgTypeEnum.IMAGE) public class ImageMsgServiceImpl implements MsgService { // ... } @Service @MsgTypeHandler(value = MsgTypeEnum.TEXT) public class TextMsgServiceImpl implements MsgService { // ... }

用一个 context 对象保存了消息类型->消息处理对象的映射关系:

@Component public class MsgServiceContext { private final Map msgServiceMap = new HashMap(); public MsgService getMsgService(Integer type) { return msgServiceMap.get(type); } public void putMsgService(Integer type, MsgService msgService) { msgServiceMap.put(type, msgService); } }

在 Spring 的启动过程中,通过解析注解,将消息类型->消息处理对象的映射关系保存到MsgServiceContext 对象中:

@Component public class MsgServiceListener implements ApplicationListener { @Override public void onApplicationEvent(ContextRefreshedEvent event) { Map beans = event.getApplicationContext().getBeansWithAnnotation(MsgTypeHandler.class); MsgServiceContext msgServiceContext = event.getApplicationContext().getBean(MsgServiceContext.class); beans.forEach((name, bean) -> { MsgTypeHandler typeHandler = bean.getClass().getAnnotation(MsgTypeHandler.class); msgServiceContext.putMsgService(typeHandler.value().getType(), (MsgService) bean); }); } }

在单元测试中进行测试:

@RunWith(SpringRunner.class) @SpringBootTest public class MsgServiceContextTest { @Autowired private MsgServiceContext msgServiceContext; @Test public void contextLoads() { MsgVo msgVo = new MsgVo(MsgTypeEnum.TEXT.getType(), "消息内容"); MsgService msgService = msgServiceContext.getMsgService(msgVo.getType()); msgService.handler(msgVo); } }

=========================================================================================== 2022-02-11 更:

2.9 使用函数式接口进行优化

基于 2.8 案例。

在 2.8 案例中,是基于 策略模式 进行优化的。这样的话,有一个明显的缺点:策略实现类会非常多!没法俯视整个分派的业务逻辑。

所以,这次的优化使用了 Map + 函数式 接口来减少类的产生。

Java 8 函数式接口:Java 8 中新增了许多函数式接口。这里,我使用的函数式接口是:Consumer:接受一个输入参数并且无返回的操作

接下来看看如何使用它~~

1、先定义一个消息类型处理器 MsgTypeHandler

@Component public class MsgTypeHandler { @Autowired private MsgTypeService msgTypeService; // Consumer:要求只有一个入参,且为 String 类型 private Map msgTypeMap = new HashMap(); @PostConstruct public void dispatcherInit() { msgTypeMap.put("文本", msgType -> msgTypeService.handlerTextMsg(msgType)); msgTypeMap.put("图片", msgType -> msgTypeService.handlerImageMsg(msgType)); } public void handler(String msgType) { Consumer consumer = msgTypeMap.get(msgType); if (null != consumer) { consumer.accept(msgType); return; } System.out.println("没有需要处理的消息类型~~"); } }

上述代码用上了 Java 8 的新特性:lambda 表达式

判断条件放在 key 中对应的业务逻辑放在 value 中(是一个方法)

这样子写的好处是非常直观,能直接看到判断条件对应的业务逻辑。

2、业务逻辑类 MsgTypeServiceImpl

@Service public class MsgTypeServiceImpl implements MsgTypeService { @Override public void handlerTextMsg(String msgType) { System.out.println("处理文本消息"); } @Override public void handlerImageMsg(String msgType) { System.out.println("处理图片消息"); } }

3、单元测试

@RunWith(SpringRunner.class) @SpringBootTest public class MsgServiceContextTest { @Autowired private MsgTypeHandler msgTypeHandler; @Test public void contextLoads2() { String msgType = "文本"; msgTypeHandler.handler(msgType); } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有